package com.google.code.sysarch.v4.vrml_loader;

import java.util.ArrayList;

import org.lwjgl.opengl.GL11;

import com.google.code.sysarch.v4.graphical.Texture;

public class VRML_Shape {

	private ArrayList<Float[]> pointsList = new ArrayList<Float[]>();
	private ArrayList<Integer[]> facesList = new ArrayList<Integer[]>();
	private ArrayList<Float[]> texturesList = new ArrayList<Float[]>();
	private ArrayList<Integer[]> texfacesList = new ArrayList<Integer[]>();
	private float[][] points;
	private int[][] faces;
	private float[][] textures;
	private int[][] texfaces;
	private float x,y,z;
	private float scale_x, scale_y, scale_z;	
	private float rot_x, rot_y, rot_z, rot_angle;
	private Texture texture;
	private boolean textureFix;
	private float red, green, blue, transparency;
	
	
	public VRML_Shape() {		
		scale_x = 1f;
		scale_y = 1f;
		scale_z = 1f;
		red = 1f;
		green = 1f;
		blue = 1f;
		transparency = 0f;
		x = 0f;
		y = 0f;
		z = 0f;
		textureFix = false;		
	}
	
	public void translate(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}	
	
	public void rotate(float rot_angle, float rot_x, float rot_y, float rot_z) {
		this.rot_x = rot_x;
		this.rot_y = rot_y;
		this.rot_z = rot_z;
		this.rot_angle = rot_angle;
	}
	
	public void scale(float x, float y, float z) {
		this.scale_x = x;
		this.scale_y = y;
		this.scale_z = z;
	}
	
	public void setTexture(Texture tex) {
		this.texture = tex;
	}
	
	public void setColor(float r, float g, float b, float a) {
		this.red = r;
		this.green = g;
		this.blue = b;
		this.transparency = a;
	}
	
	public void setTransparency(float a) {
		this.transparency = a;		
	}	
	
	public void textureFix(boolean on) {
		this.textureFix = on;	
	}
		
	public void addPoint(float x, float y, float z) {
		Float[] vertex = {x, y, z};
		pointsList.add(vertex);
	}
	
	public void addFace(Integer[] face) {
		facesList.add(face);
	}
	
	public void addTexture(float s, float t) {
		Float[] texture = {s, t};
		texturesList.add(texture);
	}
	
	public void addTexFace(Integer[] texface) {
		texfacesList.add(texface);
	}
	
	public void optimize() {
		points = new float[3][pointsList.size()];
		textures = new float[2][texturesList.size()];
		faces = new int[facesList.size()][];
		texfaces = new int[texfacesList.size()][];
		for (int i=0; i<pointsList.size(); i++) {
			points[0][i] = pointsList.get(i)[0];
			points[1][i] = pointsList.get(i)[1];
			points[2][i] = pointsList.get(i)[2];
		}
		for (int i=0; i<texturesList.size(); i++) {
			textures[0][i] = texturesList.get(i)[0];
			textures[1][i] = texturesList.get(i)[1];
		}
		for (int i=0; i<facesList.size(); i++) {
			faces[i] = new int[facesList.get(i).length];
			for (int j=0; j<facesList.get(i).length; j++) {
				faces[i][j] = facesList.get(i)[j];
			}
		}
		for (int i=0; i<texfacesList.size(); i++) {
			texfaces[i] = new int[texfacesList.get(i).length];
			for (int j=0; j<texfacesList.get(i).length; j++) {
				texfaces[i][j] = texfacesList.get(i)[j];
			}
		}
		pointsList = null;
		texturesList = null;
		facesList = null;
		texfacesList = null;
		Runtime.getRuntime().gc();
	}
	
	public void render() {
		render(this.points, null, 0);
	}
	
	public void render(float[][] from, float[][] to, double adv) {
		int point, point1, point2, point3;
		int tex;
		float[] normal = new float[3];
		float mx, my, mz;
		
		GL11.glPushMatrix();
		
		GL11.glTranslatef(x, y, z);		
		GL11.glRotatef(rot_angle, rot_x, rot_y, rot_z);
		GL11.glScalef(scale_x, scale_y, scale_z);		
		
		if (texture != null) {
			texture.bind();
		} else {
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);          	
		}
		GL11.glColor4f(red, green, blue, transparency);
		
		int counter = 0;
		boolean render = false;
		
		for (int len=3; len<=5; len++) {
			if (len == 3) {
				GL11.glBegin(GL11.GL_TRIANGLES);			
			} else if (len == 4) {
				GL11.glBegin(GL11.GL_QUADS);
			} else {
				GL11.glBegin(GL11.GL_POLYGON);
			}			
			for (int i=0; i < faces.length && counter < faces.length; i++) {			
				render = false;
				if (faces[i].length == len) {
					render = true;
				} else if (len == 5 && faces[i].length >= 5) {
					render = true;
				}
				if (render) {
					counter++;
					point1 = faces[i][0];
					point2 = faces[i][1];
					point3 = faces[i][2];
					normalize(from, normal, point1, point2, point3);
					GL11.glNormal3f(-normal[0], -normal[1], -normal[2]);						
					for (int j=0; j<faces[i].length; j++) {		
						if (texture != null) {
							if (texfaces.length <= i) {
								// No texCoordIndex defined, use coordIndex settings
								tex = faces[i][j];
							} else {
								tex = texfaces[i][j];
							}
							if (textureFix) {
								GL11.glTexCoord2f(textures[0][tex], 1-textures[1][tex]);
							} else {
								GL11.glTexCoord2f(textures[0][tex], textures[1][tex]);
							}
						}
						point = faces[i][j];
						if (to == null) {
							GL11.glVertex3f(from[0][point], from[1][point], from[2][point]);
						} else {
							mx = (float)((to[0][point] - from[0][point])*adv + from[0][point]);
							my = (float)((to[1][point] - from[1][point])*adv + from[1][point]);
							mz = (float)((to[2][point] - from[2][point])*adv + from[2][point]);
							GL11.glVertex3f(mx, my, mz);
						}
					}
				}
			}
			GL11.glEnd();
		}
		
		GL11.glPopMatrix();
	}
	
	public void normalize(float[][] from, float[] normal, int point1, int point2, int point3) {
		float a,b,c,d,e,f;
		a = from[0][point3] - from[0][point1];
		b = from[1][point3] - from[1][point1];
		c = from[2][point3] - from[2][point1];
		d = from[0][point2] - from[0][point1];
		e = from[1][point2] - from[1][point1];
		f = from[2][point2] - from[2][point1];
		normal[0] = b*f - c*e;
		normal[1] = c*d - a*f;
		normal[2] = a*e - b*d;	
	}
	
	public void _debug() {
		System.out.println("### --- NEW SHAPE --- ###");
		System.out.println("Scale: "+scale_x+" "+scale_y+" "+scale_z);
		System.out.println("Color: "+red+" "+green+" "+blue);
		System.out.println("Textured: "+(texture == null ? "no" : "yes"));
		System.out.println("Points: "+points[0].length);
		System.out.println("Faces: "+faces.length);		
		System.out.println("Texture Coords: "+textures[0].length);
		System.out.println("Texture Indizes: "+texfaces.length);
		System.out.println("Points:");
	}
}
